SECST is a Semantic, Extensible, Computational, Styleable, Tag oriented markup language. You can use it to joyfully create compelling, interactive HTML SEO enabled documents.
SECST is intended to be a more expressive, but just as easy to learn and use, alternative to Markdown. It provides these features:
:value[2 * 2] = .
<b> for bold is not semantic, but <strong> for indicating importance and rendering as bold is semantic.This site's navigation, footnotes, computations, special styles like Key formatting and imported data are built entirely using SECST markup.
This is the source for the introduction above:
:abbr[SECST] is a :strong[S]emantic, :strong[E]xtensible, :strong[C]omputational, :strong[S]tyleable :strong[T]ag oriented markup language. You can use it to joyfully create compelling, interactive, SEO enabled documents backed by HTML.
SECST is intended to be a more expressive, but just as easy to learn and use, alternative to Markdown. It provides these features:
:ul[
- Support for almost all HTML tags.
- :a(.#tables-of-contents)[Table of contents] and :a(.#footnotes)[footnote management].
- :a(.#hashtags)[Hashtag] and social media :a(.#mentions)[support].
- Reactive :a(.#values-and-formulas)[formulas and data import] that are no more complex than Excel formulas, e.g. :code[:value[2 * 2]] = :value[2 * 2].
- The use of JSON-LD :a(.#structured-data)[structured data] for both content generation/layout and SEO optimization.
- :a(.#images)[Asset bundling] for images, video, and audio.
- Server and client side well structured HTML output resistant to XSS attacks.
]
:dl[
:dt[Semantic Tag]
:dd[A tag that has meaning beyond that used to facilitate document layout and style.]
:dd[:code[<b>] for bold is not semantic, but :code[<strong>] for indicating importance and rendering as bold is semantic.]
]
This site's navigation, footnotes, computations, special styles like :kbd[Key] formatting and imported data are built entirely using SECST markup.The SECST language and reference HTML transpiler and runtime support implementations are currently in ALPHA. Most, but not all, documented features are implemented, testing is far from complete and resource sizes have not been optimized.
SECST is AGPL Licensed. Visit SECST on GitHub to participate in its further development.
static on audio, img, video and value tags.If you only need basic Markdown, then SECST is probably not for you.
If you need extended Markdown, then SECST may be a good option.
If you need more than Markdown, then SECST may be your only option short of HTML, CSS, and JavaScript so long as you are focused on interactive document authoring. If you are building an application use Svelte, VUE, React or some other framework.
See https://www.markdownguide.org/basic-syntax/.
| Document Item | Markdown | SECST | Example |
|---|---|---|---|
| headings | h# heading text | h#[heading text] | |
| bold | **text** | :strong[text] | text |
| italics | *text* | :em[text] | text |
| blockquote | > quote text | :blockquote[quote text] | |
| nested blockquote | > quote text Enter >> nested quote text | :blockquote[quote text :blockquote[nested quote text]] | |
| code | 'the code' | :code[the code] | the code |
| horizontal rule | *** | :hr | |
| newline | a new line | a new line or :br | |
| paragraph | two new lines, i.e. an empty line | two new lines or :p[<content>...] |
Where SECST can become un-necessarily verbose, e.g. lists items and table cells, it has shorthand notation similar to Markdown.
See https://www.markdownguide.org/extended-syntax/.
| Document Item | Markdown | SECST | Example |
|---|---|---|---|
| footnotes | [number] | :footnote[number] or :footnote[] for automatic numbering | |
| heading ids | # My Heading {#my-id} | :h1(#my-id)[My Heading] | |
| definition lists | see definition lists | ||
| strikethrough | ~~struck out~~ | :strike[struck out] | |
| task lists | see task lists | ||
| emojis | :emoji-name: | :emoji[emoji-name] | 😄 |
| highlight | ==marked words== | :mark[marked words] | marked words |
| subscript | H~2~O | H:sub[2]0 | H20 |
| superscript | X^2^ | X:sup[2] | X2 |
| automatic URL linking | https://my.url.com | :a[https://my.url.com] |
Authors write documents using semantic tags with the format <tag>[<content>] or <tag>(<parameters>)[<content>].
For example:
:strong[bolded important content] produces bolded important content:em[italicized emphasized content] produces italicized emphasized content Almost every semantic tag supported by HTML5 is one of the tags supported by SECST. Most non-semantic tags are excluded from SECST, e.g. <div> and <span>. Exceptions are <hr>, <table>, <ol>, <ul> and <style>.
A few tags of note are documented below to illustrate the use of SECST or highlight special features.
SECST will treat two newlines in sequence as a paragraph break. There is also a paragraph tag :p[]. Newlines inside a p tag will produce separate HTML <br> elements. A p tag is not strictly necessary for really simple content. A SECST parser/transpiler will do a reasonable job of figuring out where paragraph breaks are required; however behavior may vary from implementation to implementation.
If you get an unexpected document layout look try putting content inside a :p[] tag.
Contents wrapped in :code[] will be inline unless the contents contain a newline, in which case the code will be displayed in paragraph format.
If you have a single line you wish to show as a paragraph, end it with a newline.
Headings automatically get ids you can use for the destination of links. The id is the same as the text content in lower case with spaces and special characters replaced by dashes. To make it more convenient, you can also provide your own ids, e.g. :h1(#myheading)[My Heading] with link :a({href:"#myheading"})[Go To My Heading].
Note: All tags can optionally take an id starting with #. If an id is present, it must be the first parameter.
Sometimes, e.g. with links, you need to specify more than content. This is provided using an attribute block in parentheses, e.g.
:a({href:"./index.txt",target:"_tab"})[SECST] yields SECST , where you can access the source of this document.
For convenience, any tag that can accept a url (either src or href in HTML) does not require the attribute, just provide the URL. And, single quotes can be used for attribute values, e.g.
`:a(./index.txt {target:'_tab'})[SECST]` yields SECST.
You can also use document ids prefixed with a period as targets, e.g. .#headings for this section. The period prefix is necessary to distinguish from providing an id for the link itself.
You can also specify links with just a content value, e.g. :a({target:"_tab"})[./index.txt] renders as ./index.txt.
By default relative URLS and ids, i.e. those starting with a period, have a target that is the current window or tab. And, those starting with a protocol, e.g. https:// have a target of a new tab.
All tags that take more than just content are fully documented in the complete list of tags. Also see Advanced Authoring.
Both ordered and unordered lists are supported using ol and ul tags with li tags.
:ul[:li[LinkedIn] :li[Facebook] :li[Twitter]]
There is also a shorthand:
:ul[
- LinkedIn
- Facebook
- Twitter
] The same shorthand also works for ol. Since SECST already knows the list is ordered, numbers are inserted. The main utility of using numbers, is to reset numbering:
:ol[
2. LinkedIn
- Facebook
- Twitter
] The standard HTML attributes for configuring ordered lists, reversed, start, type are also supported.
The :meta[] tag inserts values into the head of a document. It takes the form :meta({name:"my-name"})[my value], e.g. :meta({name:"author"})[John Jones] injects the HTML
Suggested meta tag names include:
index or noindex and follow or nofollow.
Also see the section :a(."#structured-data")[Structured Data] under SEO.
Since SECST has a focus on semantics, images must be specified with alt text.
:img(static ./anywhichway_mobius_138.png)[] will not transpile.
Images can be loaded statically at transpile time and delivered bundled into the output HTML.
:img(static ./anywhichway_mobius_138.png)[Test]src attribute of the Toptal has a great reference for this.
Special characters and symbols can be displayed using either &<character-id>; or :&[<character-id>...]. The second form can display multiple characters or symbols, just leave off the leading "&" and trailing ";" in the listed items.
The code ☻ renders as ☻ .
The code :&[female male #9893] renders as ♀♂⚥.
:ins[inserted] renders as inserted
:del[deleted] renders as deleted
The :details[] tag supports the conditional display of content. It has one required sub-tag :summary[<short text>] and the rest of the content is conditionally displayed. If a summary tag is missing, then the first word is used as the summary.
:details[:summary[See the content...] Hello there!] renders as See the content...
Hello there!
:details[... Hello there!] renders as ...
Hello there!
Hashtags can take the form #<value>[] or #[<value>...], or :hashtag[<value>...] where <value>... is a space separated list of words to hashtag. The short form MUST be followed by [] to distinguish them from ids used to identify :value[] and other tags in the document.
#sects-is-great[] renders as #sects-is-great.
#[sects-is-great javascript-is-great] renders as #sects-is-great, #javascript-is-great.
:hashtag[sects-is-great javascript-is-great] renders as #sects-is-great, #javascript-is-great.
Hashtags are automatically added to the meta tags in the head of a document for indexing and SEO purposes.
Emojis can take the form :emoji[<emoji-name>...], where <emoji-name>... is a space separated list of emoji names.
There are over 1,800 emojis. If a match for the emoji name can't be found, it is displayed as plain text.
:emoji[smile] renders as 😄.
:emoji[smile frowning jumping] renders as 😄 😦 :jumping.
Since most documents are black and white, emoji's are color filtered to grayscale by default. You can use colored emojis by providing the attribute colored.
:emoji(colored)[smile] renders as :emoji(colored)[smile].
:emoji(colored)[smile frowning jumping] renders as :emoji(colored)[smile frowning jumping].
Tables of contents are placed at the location of the tag toc[<title>]. If you do not provide a title, "Table of Contents" is used. All content after the tag is analyzed for headings and used for the table. Navigation links are automatically created. Only the first occurrence of toc is used, others are ignored.
After transpilation, each heading in the document will have an &[#9783] icon preceding it. When this icon is clicked, the table of contents will hover over the heading until something in the document is clicked. Each heading will have up and down arrows placed after it to facilitate jumping to the previous and next section.
The tag can take the boolean attribute toggle to collapse the table of contents into an HTML details element.
Footnotes are inserted with the :footnote[<footnote-text>] tag. The content between the braces is used as the footnote and placed at the end of the document. Auto-numbering is done so that authors do not have to keep updating footnotes as the document changes. 1
The source for the above footnote is: :footnote(#markdown-footnote)[Markdown does not autonumber footnotes, which makes them hard to maintain. Although, it does create back-references.].
To re-use a footnote, give the first occurrence an id. Then, give the subsequent uses an href attribute using the id and leave the content section between the braces empty. This will override the anchors for numbered links generated by the SECST transpiler, but not break ordering. 2 If you do provide content when reusing, it will be ignored.
The source for the second footnote is: :footnote(.#markdown-footnote).
And, here is a third, :footnote[Footnote management does not get much easier!], that just gets numbered. 3
The HTML of transpiled SECST can be displayed as plain text, e.g. :transpiled[:strong[test]] renders as
Several platforms provide @mention capability, but only for their users, e.g. GitHub and StackExchange.
Mentions take the form @<platform>[<space separated profile-names>]. The target for mentions will always be another browser tab, unless a target attribute is provided.
The mark-up @github[anywhichway] renders as anywhichway@github.
Alternatively you can name the user and provide your own content, e.g. @github({user:"anywhichway"})[AnyWhichWay] renders as AnyWhichWay.
The supported platforms are:
:latex[Lift = \frac{1}{2} \rho v^2 S C_L\] renders as
SECST tagging inside of :code[] is automatically escaped. If you wish to escape it elsewhere use the tag :escape[].
:code[:strong[text]] renders as :strong[text].
:escape[:strong[text]] renders as :strong[text].
SECST supports the tag :value[] for adding reactive variables and formulas to documents.
:value[] is the content, i.e the text between the []. It should be either a literal value or a formula, e.g. :value[2 * 2] = .
hidden to hide them, e.g. :value(hidden)[2 * 2] = .
plaintext, e.g. :value(plaintext)[2 * 2] = .
editable, e.g. code[:value(editable)[2 * 2]] = .
[] is treated as a formula for evaluation, if your readers may be entering formula like text and you do not want it evaluated, use the attribute literal, e.g. :value(literal)[2 *2] = .
title attribute, e.g. :value({title:"Two times two"})[2 * 2], then the content is used as the mouseover title unless the content is provided by a URL, in which case the URL is the title, e.g. :value(./sample.json type="application/json") has a mouseover title of ./sample.json.
Formulas are actually just valid JavaScript expressions that also support standard and symbolic math. SECST attempts to process most values as math expressions first4 and if this fails as JavaScript.
2 * 3 = ,
2^3 when used as part of a math expression5, is the same as 2**3, which is the same as pow(2,3), which is the same as Math.pow(2,3) = .
To use a value in another formula, the value must be named with an id, e.g.
:value(#v1)[2] + :value(#v2)[2] = :value(plaintext)[$(#v1) * $(#v2)]
+ = (try changing one of the inputs)
The code $(#<some-id>) is used to substitute named values into formulas. See Advanced Formulas for use of more general CSS selectors and arrays.
The :value[] tag supports the type attribute, valid types include those supported by the HTML input element except button, file, hidden, image, search and submit.
If you provide a type, e.g. :value(editable {type:"number"}), then the type will be enforced when the value is edited: .
If the value is computed, a red border will surround type check failures.
The type can also be one of the mime-types text/plain, text/csv, application/json, e.g. :value(editable {type:"application/json"})[{name:"Joe"}] as shown below.
Strings can be combined using the + sign, e.g. :value["Hello" + " " + "world!"] = .
Strings can be manipulated using the standard JavaScript methods, e.g. :value["Hello world!".substring(0,5)] = .
editable is provided, which automatically makes the value visible.literal if you want to prevent the evaluation of the content between the [], i.e. if it is not intended to be a formula itself but looks like a formula. See the last example below. Math formulas include anything that can use the normal operators, ,%,^,*,-,+,<,<=,==,>=,> and any functions available via MathJS.
This includes these items (most links goto MathJS documentation):
Values can included imported data using the src attribute and a mime type provided in the type attribute. The trailing [] are optional. Static data is imported at transpile time.
JSON is formatted for readability and JSON5 is supported to make it easier to handle remote data.
:value(https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_hour.geojson static {type:"application/json"})
Imported data is readonly because it is essentially a computation. To make it editable, use the attribute editable.
:value(https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_hour.geojson editable {type:"application/json"})
If an id is added, the data can be used in a computation.
:value(#earthquake-data https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_hour.geojson {type:"application/json"})
:value[$(#earthquake-data).metadata]
You can use almost any CSS selector in place of #id in $(#<id>). The value returned will be the contents of the value property on the element or (if not available) the value attribute or the innerText.
You can use commas to select multiple identifier named elements, e.g. :value[$(#id1, #id2, #id3)] is an array of the values associated with the elements :value(#id1)[3], :value(#id2)[2], :value(#id3)[1] and will be [3,2,1].
A value reference $(css-selector) can end in [] to indicate multiple values should be returned as an array if the selector does not contain a comma. This allows tables to be treated somewhat like spreadsheets.
You can ...
:value[derivative('x^2 + x', 'x')]:value[simplify('x^2 + x + 3 + x^2')]:value[solve('Math.pow(r,2)',{r:3})}]:value(#v1d)[0] is :value(#v2d)[solve('Math.pow(r,2)',{r:$(#v1d)})]SECST provides several features to support SEO Search Engine Optimization.
For select tags, SECST can generate JSON-LD directly from SECST markup. Content, layout, and SEO optimization will always be in sync.
This is best covered by an example based on Google's documentation for a news article.
The SECST tag :NewsArticle[] can contain regular SECST as well as tags that parallel established JSON-LD schema like :headline[], :author[] and :Person[]. Where there are identical or trivially equivalent tags and attribute names, e.g. image and img, the native SECST tag is used.
:NewsArticle[
:headline[Pinky Conquers The World]
:img(./assets/images/The_Blue_Marble.jpg {height:50})[Earth]
:p[
Today Pinky finally conquered the world! Next up will be Mars. Watch out billionaires!
]
:author[The Brain]
:datePublished({format:""})[2022-12-20T08:00:00+08:00]
] Where JSON-LD names elements accept sub-types, e.g. :Person[], those can also be used:
:NewsArticle[
:headline[Pinky Conquers The World]
:img(./assets/images/The_Blue_Marble.jpg {height:50,align:"left"})[Earth]
:p[
Today Pinky finally conquered the world! Next up will be Mars. Watch out billionaires!
]
:author[
:Person[
:name[The Brain]
]
]
:datePublished({format:""})[2022-12-20T08:00:00+08:00]
]renders as
Today Pinky finally conquered the world! Next up will be Mars. Watch out billionaires!
SECST knows which tags to collect into arrays for JSON-LD and how to map similar SECST or HTML semantics to JSON-LD.
Some JSON-LD attributes mapped to tags can have attributes, e.g. :heading({level:2})[].
Also note below the use of Date formatting based on Javascript date and time format functionality.
:NewsArticle[
:headline({level:2})[Pinky and The Brain Plan For Mars]
:img(./assets/images/mars.jpg {height:50,align:"left"})[Mars]
:p[
We, Pinky and The Brain, will soon announce plans to partner with one of the world's billionaires to take over Mars.
]
:Author[
:Person[
:name[Pinky]
],
:Person[
:name[The Brain]
]
]
:datePublished({format:"{dateStyle:'full',timeStyle:'long'}"})[2022-12-20T08:00:00+08:00]
]will generate this JSON-LD and insert it into the head of the document:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "NewsArticle",
"headline": "Pinky and The Brain Plan For Mars",
"image": [
"./assets/images/mars.jpg"
],
"datePublished": "2022-12-20T08:00:00+08:00",
"author": [{
"@type": "Person",
"name": "Pinky",
},{
"@type": "Person",
"name": "The Brain"
}]
}
</script>while rendering this content
We, Pinky and The Brain, will soon announce plans to partner with one of the world's billionaires to take over Mars.
The ordering of the tags inside :NewsArticle[] is only relevant to the display layout. This generates equivalent JSON-LD:
:NewsArticle[
:headline({level:4})[Pinky and The Brain Plan For Mars]
:author[
:Person[:name[Pinky]],
:Person[:name[The Brain]]
]
:datePublished({format:"{dateStyle:'full',timeStyle:'long'}"})[2022-12-20T08:00:00+08:00]
:img(./assets/images/mars.jpg {height:50,align:"left"})[Mars]
:p[
We, Pinky and The Brain, will soon announce plans to partner with one of the world's billionaires to take over Mars.
]
]but renders with a smaller heading and the attribution at the start:
We, Pinky and The Brain, will soon announce plans to partner with one of the world's billionaires to take over Mars.
The generated HTML also contains styles, so a style sheet can be used to modify the displayed content.
The reference implementation of SECST provides both client and server rendering capability.
Content delivered from server rendering is identical to that rendered on the client except forumlas will not be resolved and output element sizes will not be adjusted for the target device. Upon loading, a SECST runtime library makes a pass through the document to compute formulas and adjust layout.
The ability to use styling can be configured using API options and command line arguments. See document programming below. Hence, not all styling may be available in all environments that provide a SECST editor.
Content can be styled using CSS classes loaded using the style[] tag. This tag can be used in multiple ways. If a url is provided, i.e. style(./my-css-url.css)[], then an HTML link element of type style is created behind the scenes. If CSS is placed between the square braces, i.e. style[.my-class { background-color: red}], then a style element with CSS content is created behind the scenes. If both are provided, then two elements are generated, one of each. link(<some url> type='style')[] tag.
CSS classes are applied to content using .<class-name> inside the parenthetical configuration part of a tag, e.g. :p(.my-class1 .my-class2)[My content].
Style can also be applied using the style attribute of a tag. The style attribute can be in string or JSON format. If in JSON format, should use camelCase versions of CSS property names. For example, :p(style='width:200px;text-align:right;float:right')[My right content] and :p({style:{width:"200px";textAlign:"right",float:"right"}}')[My right content] produce the same output.
My right content
Left Content
More left content
As pointed out above, the SECST macro evaluator supports regular and advanced math. It also supports some utility functions.
You can use any JavaScript that is available in WebWorkers inside a macro. A special read-only document object is also made available. This object has title, baseURI, location, and navigator properties along with a property for each meta tag name and value.
Depending on your use case, the reference transpiler can be set to do one of the following when errors are encountered.
Note, not all design objectives are fully implemented.
All SECST markup takes one of two forms:
:<tag>[<content>...] where code:[<content>] is SECST markup ans text.:<tag>(#<id> .<class>... <unaryattribute>... <attributes>...)[<content>...] where <id>, <class> space separated list, <unaryattribute> space separated list, <attributes> JSON5 object are all optional, but must be provided in that order if present. In some cases, e.g. for :hr, :br, and :value the trailing [] are optional.
The reference implementation uses a PEG parser and this grammar:
Note, not all security features are fully implemented and tested in this release
head, body.:style[] tag.
1, 2 Markdown does not autonumber footnotes, which makes them hard to maintain. Although, it does create back-references.
3 Footnote management does not get much easier!
4 A regular expression is used to match potential math expressions.
5 If the expression cannot be parsed as math, then 2^3 will be treated as XOR as it is in regular JavaScript.